﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace RPNCalculator
{
    //Command Class
    abstract class Command : ICommand
    {

        private Stack<int> ValueStack;
        private Stack<int> UndoStack = new Stack<int>();

        

      
        public int ValueStackCount()
        {
            return ValueStack.Count();
        }

       
        public int UndoStackCount()
        {
            return UndoStack.Count();
        }

  
      


        //post: valuestack!=null
        public Command(Stack<int> aStack)
        {
           
            ValueStack = aStack;
        }

       
        abstract public void Execute();

        
        //pre: undostack.count>0
        //post: valuestack.contains(undo[0..count-1])
        //post: undostack.count==0
        public void Undo()
        {
           
            // Remove the top value from ValueStack
            ValueStack.Pop();

            // Pop value from the UndoStack and Push to ValueStack.
            while (UndoStack.Count != 0)
                ValueStack.Push(UndoStack.Pop());
        }

        //pre: isvaluestacknull()==false
        protected void Push(int number)
        {
            ValueStack.Push(number);
        }

        //pre: valuestack.count>0
            
        //psot: valuestack[0...length-1]==oldelements[0...length-2]
        protected int Pop()
        {
            // Pop value from ValueStack
            int Value = ValueStack.Pop();
            // Push the value to UndoStack
            UndoStack.Push(Value);          
            return Value;
        }

        
        public bool IsUndoStackNull()
        {
            return UndoStack == null;
        }

        public bool IsValueStackNull()
        {
            return ValueStack == null;
        }







        public bool IsEmpty()
        {
            return ValueStack.Count == 0;
        }
    }


    //Sub Command Class 1: PushCommand 
    class PushCommand : Command
    {
        private int _value;

        static public bool IsExecutable(int ValueCount)
        {
            return ValueCount >= 0;
        }

        public PushCommand(Stack<int> aStack, int value)
            : base(aStack)
        {
            
            _value = value;
        }

        //executes the push command and puts the value onto the stack
        //pre: valuestack!=null
        //pre: undostack != null
        //post: oldvaluestack[0...count-1] == valuestack[0...count-2]
        //post: oldundostack[0...count-1] == undostack[0...count-2]
        public override void Execute()
        {
            Push(_value);
        }
    }
    //Calculates the two top numbers in the stack
    //Sub Command Class 2 : BinaryCommand 
    abstract class SimpleCommand : Command
    {
        public SimpleCommand(Stack<int> aStack)
            : base(aStack)
        {
            
        }
        //import previous pre/post conditoins and invariants
        //post: valuestack[0...length-2]=oldvaluestack[0...length-3]
        //post: valuestack.top!=oldvaluestack.top-1
        //Execute()


        static public bool IsExecutable(int ValueCount)
        {
            return ValueCount >= 2;
        }
    }
    


    //Sub Command Class 3: MultaryCommand 
    abstract class MultaryCommand : Command
    {
        public MultaryCommand(Stack<int> aStack)
            : base(aStack)
        {
           
        }

        static public bool IsExecutable(int ValueCount)
        {
            return ValueCount >= 2;
        }

        abstract protected int SpecificExecute(int i, int j);

        //Executes a command using all the numbers in the stack to calculate one number
        //pre: valuestack>=2
        //inv: valuestack>0
        //post: valuestack.count==1
        //post: oldUndoStack=valuestack
        
        public override void Execute()
        {
            Contract.Requires(ValueStackCount() >= 2);
            Contract.Ensures(ValueStackCount() == 1);
            Contract.Ensures(UndoStackCount() == Contract.OldValue(ValueStackCount()));

            int i = Pop();
            while (!IsEmpty())
            {
                i = SpecificExecute(i, Pop());
            }
            //Push the result
            Push(i);       

        }
    }

    //Takes the two values at the top of the stack and replaces them with the value you get when you add them together
    //Sub BinaryCommand Class: AddCommand 
    class AddCommand : SimpleCommand
    {
        public AddCommand(Stack<int> aStack)
            : base(aStack)
        {
            Contract.Requires(aStack != null);
        }

        
        public override void Execute()
        { 
           
            Contract.Ensures(ValueStackCount() >= 0);
            // Push the sum of the 2 poped values
            Push(Pop() + Pop()); 
        }
    }
    //Takes the two values at the top of the stack and replaces them with the value you get when you substract them from each other
    //Sub BinaryCommand Class: SubtractCommand
    class SubtractCommand : SimpleCommand
    {
        public SubtractCommand(Stack<int> aStack)
            : base(aStack)
        {
            
        }

        public override void Execute()
        { 
            Push(-Pop() + Pop()); 
        }
    }
    //Takes the two values at the top of the stack and replaces them with the value you get when you Multiply them with each other
    //Sub BinaryCommand Class: MultiplyCommand 
    class MultiplyCommand : SimpleCommand
    {
        public MultiplyCommand(Stack<int> aStack)
            : base(aStack)
        {
            
        }

        public override void Execute()
        { 
            Push(Pop() * Pop()); 
        }
    }
    //Takes the two values at the top of the stack and replaces them with the value you get when you divide them with each other
    // Sub BinaryCommand Class: DivideCommand
    class DivideCommand : SimpleCommand
    {
        public DivideCommand(Stack<int> aStack)
            : base(aStack)
        {
            
        }

        public override void Execute()
        {
            int divisor = Pop();
            Push(Pop() / divisor);
        }
    }
    //Takes all values from the stack and add them together To create the new stack
    //Sub MultaryCommand Class: SummaCommand 
    class SummaCommand : MultaryCommand
    {
        public SummaCommand(Stack<int> aStack)
            : base(aStack)
        {
            
        }

        protected override int SpecificExecute(int i, int j)
        {
            return i + j;
        }
    }
    //Takes all values from the stack and multiplies them with each other to create the new stack
    //Sub MultaryCommand Class: MulCommand 
    class MulCommand : MultaryCommand
    {
        public MulCommand(Stack<int> aStack)
            : base(aStack)
        {
           
        }

        protected override int SpecificExecute(int i, int j)
        {
            return i * j;
        }
    }
}
